package compiler.symboltable;

import java.util.ArrayList;

public class SymbolTable {
	/**
	 *　　This class is the Entry of symbol table
	 */

		public enum ItemType {
			INT, ARRAY, FUNC, PARAM
		}
	
		public class Item {


			//common members
			private String name;		
			private ItemType type;			   // type：INT, ARRAY or Function
			
			
			//special members
			private SymbolTable funcST;    //symbol table of function
			private int length;            //length of array
			private int value;			   // value, only for int
			
			//location member
			//private int lineno;
			//private int position;
			
			public Item(String name,  ItemType t)
			{
				this.name = name;
				//this.lineno = l;
				//this.position = p;
				this.type = t;
			}
			
			//public int getLineno()
			//{
			//	return lineno;
			//}
			
			/*public int getPosition()
			{
				return position;
			}
			public void setPosition(int p)
			{
				position = p;
			}*/
			
			public String getName()
			{
				return name;
			}
			public void setName(String n)
			{
				name = n;
			}
			
			public ItemType getType()
			{
				return type;
			}
			public void setType(ItemType t)
			{
				type = t;
			}
			
			public int getValue()
			{
				return value;
			}
			public void setValue(int v)
			{
				value = v;
			}
			
			public int getLength()
			{
				return length;
			}
			public void setLength(int l)
			{
				length = l;
			}
			
			
			public SymbolTable getFuncST()
			{
				return funcST;
			}
			public void setFuncST(SymbolTable st)
			{
				funcST = st;
			}
			//...
		}

		private String name;
		private ArrayList<Item> table;
		private SymbolTable parent;
		
		public SymbolTable(String name, SymbolTable parent) {
			this.name = name;
			this.parent = parent;
			table = new ArrayList<Item>();
		}
		
		public String getName() {
			return name;
		}
		
		public boolean isValid(String ident) {
			// check if the 'ident' is valid variable name in this scope
			// It's valid if it appears in local table and parent's table
			boolean find = false;
			if(parent != null)
				find = parent.isValid(ident);
			if(position(ident)!=-1)
				find = true;
			return find;

		}
		
		/**
		 * add an Int entry to the symbol table
		 * @throws Exception 
		 * 
		 */
		public void addIntItem(String name) throws Exception
		{
			if((position(name)!=-1)&&(table.get(position(name)).getType()==ItemType.INT))
				throw new Exception("Variable already exits!");
			Item item = new Item(name,ItemType.INT);
			this.table.add(item);
		}
		public boolean isType(String ident, ItemType type) {
			// check if the item with name 'ident' is type 'type'
			boolean match = false;
			if(position(ident)!=-1)
			{
				int idx = position(ident);
				if(this.table.get(idx).getType()==type||
				 ((this.table.get(idx).getType()==ItemType.PARAM)&&(type==ItemType.INT))||
				 ((this.table.get(idx).getType()==ItemType.INT)&&(type==ItemType.PARAM)))
					match = true;
			}
			else if(parent!=null)
				match = parent.isType(ident, type);
			return match;
		}
		
		/**
		 * add an Array entry to the symbol table
		 * @throws Exception 
		 * 
		 */
		public void addArrayItem(String name,int len) throws Exception {
			
			if((position(name)!=-1)&&(table.get(position(name)).getType()==ItemType.ARRAY))
				throw new Exception("Variable already exits!");
			Item item = new Item(name,ItemType.ARRAY);
			item.setLength(len);
			table.add(item);
		}
		/**
		 * add an parameter entry to the symbol table
		 * @throws Exception 
		 * 
		 */
		public void addParamItem(String name) throws Exception {
			if((position(name)!=-1)&&(table.get(position(name)).getType()==ItemType.INT||
									  table.get(position(name)).getType()==ItemType.PARAM))
				throw new Exception("Variable " + name + " already exits!");
			Item item = new Item(name, ItemType.PARAM);
			table.add(item);
		}

		
		/**
		 * add an Function entry to the symbol table
		 * @throws Exception 
		 * 
		 */
		public void addFuncItem(String name, SymbolTable st) throws Exception
		{
			if(position(name)!=-1 && table.get(position(name)).type == ItemType.FUNC)
				throw new Exception("Function " + name + " already exits!");
			Item item = new Item(name,ItemType.FUNC);
			item.setFuncST(st);
			
			table.add(item);
			
		}
		
		/**
		 * delete an entry from the symbol table
		 * 
		 */
		public void delete(String name, ItemType type) throws Exception
		{
			boolean find = false;
			for(int i = 0; i < table.size(); i ++)
			{
				if((table.get(i).name == name) && (table.get(i).type == type))
				{
					table.remove(i);

					find = true;
				}
					
			}
			if(!find)
				throw new Exception("Can't delete symbol when not finding!\n");
		}
		
		
		/**
		 * print table 
		 * 
		 */
		public void debugTable() {

			System.out.println("TABLE:");
			if (table.size()==0)
				System.out.println("    NULL");
			for (int i=0; i<= table.size(); i++) {
				String msg = "OOPS! UNKNOWN TABLE ITEM!";
				switch (table.get(i).type) {
				case INT:
					msg = "    " + i + " int\t" + table.get(i).name + " val=" + table.get(i).value;
					break;
				case ARRAY:
					msg = "    " + i + " array\t" + table.get(i).name + " length=" + table.get(i).length;
					break;
				case FUNC:
					msg = "    " + i + " proc  " + table.get(i).name;
					break;
				case PARAM:
					// TODO: implement
				}
				System.out.println(msg);
			}
			System.out.println();
		}

		/**
		 * find the position of a symbol in the symbol table
		 * @param idt: the name of symbol to be found
		 * @return if find this symbol, return index, else return 0
		 */
		public int position(String idt) {
			if(table.size()>0)
				for (int i = 0; i < table.size(); i ++)
					if (table.get(i).name.equals(idt))
						return i;
			return -1;
		}
		
		public int size()
		{
			return this.table.size();
		}
		
		public Item get(int idx)
		{
			return this.table.get(idx);
		}
		
		public ArrayList<Item> getTable()
		{
			return table;
		}
		
		public SymbolTable getParent()
		{
			return parent;
		}
	
}
